<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="李修倩在 Github 上的个人博客">
    <meta name="keyword" content="null">
    <meta name="theme-color" content="#600090">
    <meta name="msapplication-navbutton-color" content="#600090">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="#600090">
    <link rel="shortcut icon" href="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-person-128.png">
    <link rel="alternate" type="application/atom+xml" title="Xiuqianli&#39;s blog" href="/atom.xml">
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.css">
    <title>
        
        ThreadLocal源码泛读笔记｜xiuqianli&#39;s blog
        
    </title>

    <link rel="canonical" href="https://981764793.gitee.io/2018/07/18/ThreadLocal源码泛读笔记/">

    <!-- Bootstrap Core CSS -->
    <link rel="stylesheet" href="/css/bootstrap.min.css">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/blog-style.css">

    <!-- Pygments Github CSS -->
    <link rel="stylesheet" href="/css/syntax.css">
</head>

<style>

    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }
</style>
<!-- hack iOS CSS :active style -->
<body ontouchstart="" class="animated fadeIn">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top " id="nav-top" data-ispost = "true" data-istags="false
" data-ishome = "false" >
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand animated pulse" href="/">
                <span class="brand-logo">
                    Xiuqianli&#39;s blog
                </span>
                's Blog
            </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <!-- /.navbar-collapse -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
					
                    
                        
							
                        <li>
                            <a href="/Tags/">Tags</a>
                        </li>
							
						
                    
					
					
                </ul>
            </div>
        </div>
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
//    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>

<!-- Main Content -->

<!--only post-->


<img class="wechat-title-img"
     src="http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80">


<style>
    
    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }

    
</style>

<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <div class="post-heading">
                    <h1>ThreadLocal源码泛读笔记</h1>
                    
                    <span class="meta">
                         作者 Xiuqian Li
                        <span>
                          日期 2018-07-18
                         </span>
                    </span>
                    <div class="tags text-center">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#jdk源码"
                           title="jdk源码">jdk源码</a>
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="post-title-haojen">
        <span>
            ThreadLocal源码泛读笔记
        </span>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- Post Container -->
            <div class="col-lg-8 col-lg-offset-1 col-sm-9 post-container">
                <h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>偶尔看到一个面试题似乎是跟ThreadLocal源码相关的，所以尝试自己看一下源码试着理解一下。网上相关的博客有很多，这个只是作为自己强化记忆的一个笔记。</p>
<p>决定从最常使用的两个方法get、set开始往下看</p>
<h4 id="threadLocal-get"><a href="#threadLocal-get" class="headerlink" title="threadLocal.get()"></a>threadLocal.get()</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public T get() &#123;</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">    if (map != null) &#123;</span><br><span class="line">        ThreadLocalMap.Entry e = map.getEntry(this);</span><br><span class="line">        if (e != null) &#123;</span><br><span class="line">            @SuppressWarnings(&quot;unchecked&quot;)</span><br><span class="line">            T result = (T)e.value;</span><br><span class="line">            return result;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    return setInitialValue();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ThreadLocalMap getMap(Thread t) &#123;</span><br><span class="line">    return t.threadLocals;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">private T setInitialValue() &#123;</span><br><span class="line">    T value = initialValue();</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">    if (map != null)</span><br><span class="line">        map.set(this, value);</span><br><span class="line">    else</span><br><span class="line">        createMap(t, value);</span><br><span class="line">    return value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>先是获取当前的线程，然后调用getMap(t)获取当前线程的一个成员变量threadLocals，这个变量的类型是ThreadLocalMap，见名知义，这就是个map，估摸着跟hashmap差不多。</p>
<p>拿到这个变量后先判断是否为null，如果不为null就直接以当前ThreadLocal对象为key从map里取值，如果是null的话就调用setInitialValue()尝试设置初始值，除了调用initialValue();和return外的其他代码和set一模一样，一会再看。</p>
<p>至于initialValue()的话是可以在new ThreadLocal的时候重写的，也可以直接ThreadLocal.withInitial(Supplier&lt;? extends S&gt; supplier)，这个方法new的是一个叫SuppliedThreadLocal的子类，其中也是重写了initialValue方法</p>
<h4 id="threadLocal-set"><a href="#threadLocal-set" class="headerlink" title="threadLocal.set"></a>threadLocal.set</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public void set(T value) &#123;</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">    if (map != null)</span><br><span class="line">        map.set(this, value);</span><br><span class="line">    else</span><br><span class="line">        createMap(t, value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void createMap(Thread t, T firstValue) &#123;</span><br><span class="line">    t.threadLocals = new ThreadLocalMap(this, firstValue);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>setInitialValue里有一段代码跟set一模一样，看上去好像不太面向对象，估摸着设计者是考虑到以后的修改扩展或者是为了少调用一次函数优化一点点而直接复制的代码。</p>
<p>set跟get类似，也是先拿到当前线程的threadLocals这个map，如果不为null的话就直接以当前ThreadLocal对象为key往map里填值。</p>
<p>map为null就把当前线程对象和value传给createMap创建一个新的ThreadLocalMap对象，并把这个对象赋给当前线程的threadLocals。</p>
<h4 id="ThreadLocalMap"><a href="#ThreadLocalMap" class="headerlink" title="ThreadLocalMap"></a>ThreadLocalMap</h4><p>其实ThreadLocal本身的api并没有什么难度，重点还在ThreadLocalMap的源码</p>
<p>和HashMap类似它里面有一个内部类Entry,继承了WeakReference，以ThreadLocal对象的弱引用作为key，至于使用弱引用的话就是为了方便ThreadLocalMap的垃圾回收。</p>
<p>ThreadLocalMap中维护了一个环形Entry数组，使用<strong>线性探测法</strong>解决hash碰撞。</p>
<p>用nextIndex、prevIndex计算环形数据的上一个、下一个下标</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private static int nextIndex(int i, int len) &#123;</span><br><span class="line">        return ((i + 1 &lt; len) ? i + 1 : 0);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Decrement i modulo len.</span><br><span class="line">     */</span><br><span class="line">    private static int prevIndex(int i, int len) &#123;</span><br><span class="line">        return ((i - 1 &gt;= 0) ? i - 1 : len - 1);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<h5 id="getEntry"><a href="#getEntry" class="headerlink" title="getEntry"></a>getEntry</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private Entry getEntry(ThreadLocal&lt;?&gt; key) &#123;</span><br><span class="line">        int i = key.threadLocalHashCode &amp; (table.length - 1);</span><br><span class="line">        Entry e = table[i];</span><br><span class="line">        if (e != null &amp;&amp; e.get() == key)</span><br><span class="line">            return e;</span><br><span class="line">        else</span><br><span class="line">            return getEntryAfterMiss(key, i, e);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>被ThreadLocal直接调用，也是先计算hash，得到索引尝试直接从Entry数组里取值，如果hash碰撞了的话就调用getEntryAfterMiss进行取值。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private Entry getEntryAfterMiss(ThreadLocal&lt;?&gt; key, int i, Entry e) &#123;</span><br><span class="line">        Entry[] tab = table;</span><br><span class="line">        int len = tab.length;</span><br><span class="line"></span><br><span class="line">        while (e != null) &#123;</span><br><span class="line">            ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line">            if (k == key)</span><br><span class="line">                return e;</span><br><span class="line">            if (k == null)</span><br><span class="line">                expungeStaleEntry(i);</span><br><span class="line">            else</span><br><span class="line">                i = nextIndex(i, len);</span><br><span class="line">            e = tab[i];</span><br><span class="line">        &#125;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>这个方法就是在环形数组里一直往下找，遇上空值就代表找不到这个key对应的值了，跳出循环返回null，其中有一段判断key是否为null，key为null的话代表这个位置的ThreadLocal对象已经被清理了，调用expungeStaleEntry(i)的作用就是从i位置开始清理这些key已经被清理的数据。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private int expungeStaleEntry(int staleSlot) &#123;</span><br><span class="line">            Entry[] tab = table;</span><br><span class="line">            int len = tab.length;</span><br><span class="line"></span><br><span class="line">            // expunge entry at staleSlot</span><br><span class="line">            tab[staleSlot].value = null;</span><br><span class="line">            tab[staleSlot] = null;</span><br><span class="line">            size--;</span><br><span class="line"></span><br><span class="line">            // Rehash until we encounter null</span><br><span class="line">            Entry e;</span><br><span class="line">            int i;</span><br><span class="line">            for (i = nextIndex(staleSlot, len);</span><br><span class="line">                 (e = tab[i]) != null;</span><br><span class="line">                 i = nextIndex(i, len)) &#123;</span><br><span class="line">                ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line">                if (k == null) &#123;</span><br><span class="line">                    e.value = null;</span><br><span class="line">                    tab[i] = null;</span><br><span class="line">                    size--;</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    int h = k.threadLocalHashCode &amp; (len - 1);</span><br><span class="line">                    if (h != i) &#123;</span><br><span class="line">                        tab[i] = null;</span><br><span class="line"></span><br><span class="line">                        // Unlike Knuth 6.4 Algorithm R, we must scan until</span><br><span class="line">                        // null because multiple entries could have been stale.</span><br><span class="line">                        while (tab[h] != null)</span><br><span class="line">                            h = nextIndex(h, len);</span><br><span class="line">                        tab[h] = e;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            return i;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>
<p>清理完那个位置的数据后开始往后遍历，直到entry为null，将所有key为null的entry.value置为null，数组的对应位置tab[i]也置为null。如果是发现了key还活着的数据就重新计算hash,把数据挪过去。</p>
<h5 id="set"><a href="#set" class="headerlink" title="set"></a>set</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private void set(ThreadLocal&lt;?&gt; key, Object value) &#123;</span><br><span class="line"></span><br><span class="line">        // We don&apos;t use a fast path as with get() because it is at</span><br><span class="line">        // least as common to use set() to create new entries as</span><br><span class="line">        // it is to replace existing ones, in which case, a fast</span><br><span class="line">        // path would fail more often than not.</span><br><span class="line"></span><br><span class="line">        Entry[] tab = table;</span><br><span class="line">        int len = tab.length;</span><br><span class="line">        int i = key.threadLocalHashCode &amp; (len-1);</span><br><span class="line"></span><br><span class="line">        for (Entry e = tab[i];</span><br><span class="line">             e != null;</span><br><span class="line">             e = tab[i = nextIndex(i, len)]) &#123;</span><br><span class="line">            ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line"></span><br><span class="line">            if (k == key) &#123;</span><br><span class="line">                e.value = value;</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            if (k == null) &#123;</span><br><span class="line">                replaceStaleEntry(key, value, i);</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        tab[i] = new Entry(key, value);</span><br><span class="line">        int sz = ++size;</span><br><span class="line">        if (!cleanSomeSlots(i, sz) &amp;&amp; sz &gt;= threshold)</span><br><span class="line">            rehash();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>同样先是计算hash，用一个for循环实现线性探测法在发生hash碰撞的时候往后寻址。</p>
<p>遇上key相等的直接覆盖数组对应位置entry的value，如果遇上key被清理掉的就调用replaceStaleEntry(key, value, i)进行替换，这个一长串暂时看着头大没往下看。</p>
<p>如果没有碰撞就直接tab[i] = new Entry(key, value)。</p>
<p>最后调用cleanSomeSlots(i, sz)尝试清理一部分数据然后再判断是否需要重新计算hash，最后如果map大小依旧超过阈值的话就调用resize()进行扩容。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private boolean cleanSomeSlots(int i, int n) &#123;</span><br><span class="line">        boolean removed = false;</span><br><span class="line">        Entry[] tab = table;</span><br><span class="line">        int len = tab.length;</span><br><span class="line">        do &#123;</span><br><span class="line">            i = nextIndex(i, len);</span><br><span class="line">            Entry e = tab[i];</span><br><span class="line">            if (e != null &amp;&amp; e.get() == null) &#123;</span><br><span class="line">                n = len;</span><br><span class="line">                removed = true;</span><br><span class="line">                i = expungeStaleEntry(i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; while ( (n &gt;&gt;&gt;= 1) != 0);</span><br><span class="line">        return removed;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>这个清理的原理就是从新增数据的位置开始往后找√size次，如果发现了entry值为null的就调用expungeStaleEntry清理数据。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">private void resize() &#123;</span><br><span class="line">        Entry[] oldTab = table;</span><br><span class="line">        int oldLen = oldTab.length;</span><br><span class="line">        int newLen = oldLen * 2;</span><br><span class="line">        Entry[] newTab = new Entry[newLen];</span><br><span class="line">        int count = 0;</span><br><span class="line"></span><br><span class="line">        for (int j = 0; j &lt; oldLen; ++j) &#123;</span><br><span class="line">            Entry e = oldTab[j];</span><br><span class="line">            if (e != null) &#123;</span><br><span class="line">                ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line">                if (k == null) &#123;</span><br><span class="line">                    e.value = null; // Help the GC</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    int h = k.threadLocalHashCode &amp; (newLen - 1);</span><br><span class="line">                    while (newTab[h] != null)</span><br><span class="line">                        h = nextIndex(h, newLen);</span><br><span class="line">                    newTab[h] = e;</span><br><span class="line">                    count++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        setThreshold(newLen);</span><br><span class="line">        size = count;</span><br><span class="line">        table = newTab;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>扩容很简单，就是new一个当前长度乘2的entry数组，把旧数组里的值遍历放到新数组里，遍历的同时也把key为null的数据干掉。</p>

                <hr>
                

                <ul class="pager">
                    
                    <li class="previous">
                        <a href="/2018/07/18/JVM类加载简单笔记/" data-toggle="tooltip" data-placement="top"
                           title="JVM类加载简单笔记">&larr; Previous Post</a>
                    </li>
                    
                    
                    <li class="next">
                        <a href="/2018/07/18/ArrayList-HashMap源码泛读笔记/" data-toggle="tooltip" data-placement="top"
                           title="ArrayList HashMap源码泛读笔记">Next Post &rarr;</a>
                    </li>
                    
                </ul>

                

                


                <!--加入新的评论系统-->
                
            </div>

            <div class="hidden-xs col-sm-3 toc-col">
                <div class="toc-wrap">
                    <ol class="toc"><li class="toc-item toc-level-4"><a class="toc-link" href="#前言"><span class="toc-text">前言</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#threadLocal-get"><span class="toc-text">threadLocal.get()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#threadLocal-set"><span class="toc-text">threadLocal.set</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ThreadLocalMap"><span class="toc-text">ThreadLocalMap</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#getEntry"><span class="toc-text">getEntry</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#set"><span class="toc-text">set</span></a></li></ol></li></ol>
                </div>
            </div>
        </div>

        <div class="row">
            <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5 class="text-center">
                        <a href="/tags/">FEATURED TAGS</a>
                    </h5>
                    <div class="tags">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#jdk源码"
                           title="jdk源码">jdk源码</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
            </div>
        </div>

    </div>
</article>







<!-- Footer -->
<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <br>
                <ul class="list-inline text-center">
                
                
                
                    <li>
                        <a target="_blank" href="https://www.zhihu.com/people/asdf1234-97">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa  fa-stack-1x fa-inverse">知</i>
                            </span>
                        </a>
                    </li>
                

                

                

                
                    <li>
                        <a target="_blank"  href="https://github.com/xiuqianli1996">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; Xiuqianli&#39;s blog 2018
                    <br>
                    <span id="busuanzi_container_site_pv" style="font-size: 12px;">PV: <span id="busuanzi_value_site_pv"></span> Times</span>
                    <br>
                    Theme by <a href="https://haojen.github.io/">Haojen Ma</a>
                </p>

            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->
<script src="/js/jquery.min.js"></script>

<!-- Bootstrap Core JavaScript -->
<script src="/js/bootstrap.min.js"></script>

<!-- Custom Theme JavaScript -->
<script src="/js/blog.js"></script>

<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>

<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if($('#tag_cloud').length !== 0){
        async("https://981764793.gitee.io/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>

<!-- Google Analytics -->



<!-- Baidu Tongji -->


<!-- swiftype -->
<script type="text/javascript">
  (function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
  (w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
  e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
  })(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

  _st('install','null','2.0.0');
</script>

<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<!--wechat title img-->
<img class="wechat-title-img" src="null">
</body>

</html>
